module dgl.graphics.SpriteBatch;

import derelict.opengl.gl;

import dgl.Matrix;
import dgl.Rectangle;
import dgl.Vector2;
import dgl.Vector4;
import dgl.graphics.Color;
import dgl.graphics.Texture2D;

class SpriteBatch
{
	private Vector2 m_screenSize;
	
	private bool m_isBegin;
	
	private bool m_isEnableDepthTest;
	
	public Vector2 screenSize()
	{
		return m_screenSize;
	}
	
	public this( Vector2 screenSize )
	{
		m_screenSize = screenSize;
		m_isBegin = false;
		m_isEnableDepthTest = false;
	}
	
	public void begin()
	{
		if( m_isBegin )
			throw new Exception( "end() must be called before begin." );
		
		glMatrixMode( GL_PROJECTION );
		glPushMatrix();
		glLoadIdentity();
		glOrtho( 0.0f, m_screenSize.x, m_screenSize.y, 0.0f, -1.0f, 1.0f );

		glMatrixMode( GL_MODELVIEW );
		glPushMatrix();
		glLoadIdentity();
		
		GLboolean depthTest = glIsEnabled( GL_DEPTH_TEST );
		if( depthTest == GL_TRUE )
		{
			m_isEnableDepthTest = true;
		}
		else
		{
			m_isEnableDepthTest = false;
		}
		glDisable( GL_DEPTH_TEST );
		
		m_isBegin = true;
	}
	
	public void end()
	{
		if( m_isBegin == false )
			throw new Exception( "begin() must called before end." );
		
		glMatrixMode( GL_MODELVIEW );
		glPopMatrix();
		glMatrixMode( GL_PROJECTION );
		glPopMatrix();
		
		if( m_isEnableDepthTest )
		{
			glEnable( GL_DEPTH_TEST );
		}
		else
		{
			glDisable( GL_DEPTH_TEST );
		}
		
		m_isBegin = false;
	}
	
	public void draw( Texture2D texture, Vector2 position )
	{
		this.draw( texture, 
			   	   Rectangle( cast(int)position.x, cast(int)position.y, texture.width, texture.height ), 
			   	   Rectangle( 0, 0, texture.width, texture.height ), 
			   	   Color.white );
	}
	
	public void draw( Texture2D texture, Vector2 position, float alpha )
	{
		this.draw( texture, 
		   	   	   Rectangle( cast(int)position.x, cast(int)position.y, texture.width, texture.height ), 
		   	   	   Rectangle( 0, 0, texture.width, texture.height ), 
		   	   	   Color( 255, 255, 255, cast(int)( alpha * 255 ) ) );
	}
	
	public void draw( Texture2D texture, Vector2 position, Color color )
	{
		this.draw( texture, 
				   Rectangle( cast(int)position.x, cast(int)position.y, texture.width, texture.height ), 
				   Rectangle( 0, 0, texture.width, texture.height ), 
				   color );
	}
	
	public void draw( Texture2D texture, Rectangle rect )
	{
		this.draw( texture, rect, Rectangle( 0, 0, texture.width, texture.height ), Color.white );
	}
	
	public void draw( Texture2D texture, Rectangle rect, float alpha )
	{
		this.draw( texture, rect, Rectangle( 0, 0, texture.width, texture.height ), Color( 255, 255, 255, cast(int)( alpha * 255 ) ) );
	}
	
	public void draw( Texture2D texture, Rectangle rect, Color color )
	{
		this.draw( texture, rect, Rectangle( 0, 0, texture.width, texture.height ), color );
	}
	
	public void draw( Texture2D texture, Vector2 position, Rectangle sourceRect )
	{
		this.draw( texture, 
				   Rectangle( cast(int)position.x, cast(int)position.y, texture.width, texture.height ), 
				   sourceRect, 
				   Color.white );
	}
	
	public void draw( Texture2D texture, Vector2 position, Rectangle sourceRect, float alpha )
	{
		this.draw( texture, 
				   Rectangle( cast(int)position.x, cast(int)position.y, texture.width, texture.height ), 
				   sourceRect, 
				   Color( 255, 255, 255, cast(int)( alpha * 255 ) ) );
	}
	
	public void draw( Texture2D texture, Vector2 position, Rectangle sourceRect, Color color )
	{
		this.draw( texture, 
				   Rectangle( cast(int)position.x, cast(int)position.y, texture.width, texture.height ), 
				   sourceRect, 
				   color );
	}
	
	public void draw( Texture2D texture, Rectangle destinationRect, Rectangle sourceRect )
	{
		this.draw( texture, destinationRect, sourceRect, Color.white );
	}
	
	public void draw( Texture2D texture, Rectangle destinationRect, Rectangle sourceRect, float alpha )
	{
		this.draw( texture, destinationRect, sourceRect, Color( 255, 255, 255, cast(int)( alpha * 255 ) ) );
	}
	
	public void draw( Texture2D texture, Rectangle destinationRect, Rectangle sourceRect, Color color )
	in
	{
		assert( m_isBegin );
		assert( texture );
	}
	body
	{
		float texLeft = cast(float)sourceRect.left / cast(float)texture.width;
		float texRight = cast(float)sourceRect.right / cast(float)texture.width;
		float texTop = cast(float)sourceRect.top / cast(float)texture.height;
		float texBottom = cast(float)sourceRect.bottom / cast(float)texture.height;
				
		glBindTexture( GL_TEXTURE_2D, texture.id );

		Vector4 texColor = color.toVector4();
		
		glBegin( GL_QUADS );

		glTexCoord2f( texLeft, texTop );
		glColor4fv( texColor.ptr );
		glVertex3f( destinationRect.left, destinationRect.top, 0 );

		glTexCoord2f( texRight, texTop );
		glColor4fv( texColor.ptr );
		glVertex3f( destinationRect.right, destinationRect.top, 0 );

		glTexCoord2f( texRight, texBottom );
		glColor4fv( texColor.ptr );
		glVertex3f( destinationRect.right, destinationRect.bottom, 0 );

		glTexCoord2f( texLeft, texBottom );
		glColor4fv( texColor.ptr );
		glVertex3f( destinationRect.left, destinationRect.bottom, 0 );

		glEnd();
	}
	
	public void draw( Texture2D texture, Matrix transform )
	{
		this.draw( texture, transform, Rectangle( 0, 0, texture.width, texture.height ), Color.white );
	}
	
	public void draw( Texture2D texture, Matrix transform, float alpha )
	{
		this.draw( texture, transform, Rectangle( 0, 0, texture.width, texture.height ), Color( 255, 255, 255, cast(int)( alpha * 255 ) ) );
	}
	
	public void draw( Texture2D texture, Matrix transform, Color color )
	{
		this.draw( texture, transform, Rectangle( 0, 0, texture.width, texture.height ), color );
	}
	
	public void draw( Texture2D texture, Matrix transform, Rectangle sourceRect )
	{
		this.draw( texture, transform, sourceRect, Color.white );
	}
	
	public void draw( Texture2D texture, Matrix transform, Rectangle sourceRect, float alpha )
	{
		this.draw( texture, transform, sourceRect, Color( 255, 255, 255, cast(int)( alpha * 255 ) ) );
	}
	
	public void draw( Texture2D texture, Matrix transform, Rectangle sourceRect, Color color )
	in
	{
		assert( m_isBegin );
		assert( texture );
	}
	body
	{		
		float texLeft = cast(float)sourceRect.left / cast(float)texture.width;
		float texRight = cast(float)sourceRect.right / cast(float)texture.width;
		float texTop = cast(float)sourceRect.top / cast(float)texture.height;
		float texBottom = cast(float)sourceRect.bottom / cast(float)texture.height;
				
		glBindTexture( GL_TEXTURE_2D, texture.id );

		Vector4 texColor = color.toVector4();
		
		glMatrixMode( GL_MODELVIEW );
		glPushMatrix();
		glLoadMatrixf( transform.ptr );
		
		glBegin( GL_QUADS );

		glTexCoord2f( texLeft, texTop );
		glColor4fv( texColor.ptr );
		glVertex3f( 0, 0, 0 );

		glTexCoord2f( texRight, texTop );
		glColor4fv( texColor.ptr );
		glVertex3f( texture.width, 0, 0 );

		glTexCoord2f( texRight, texBottom );
		glColor4fv( texColor.ptr );
		glVertex3f( texture.width, texture.height, 0 );

		glTexCoord2f( texLeft, texBottom );
		glColor4fv( texColor.ptr );
		glVertex3f( 0, texture.height, 0 );

		glEnd();
		
		glPopMatrix();
	}
}